package main

import (
	"context"
	"fmt"
	"time"
)

func simpleWork() {
	for i := 0; i < 10; i++ {
		fmt.Printf("%d ", i)
	}
}

func channWorkA(ch chan int) {
	// 假设以下情形：
	// channWorkA和main配合工作，main需要等待channWorkA完成一部分工作，把结果给main后channWorkA再完成剩余工作。
	// main做一部分工作，然后等待channWorkA的结果，然后再继续工作。
	fmt.Println("channWorkA 完成了第一部分的工作，并将结果1发往通道")
	ch <- 1
	fmt.Println("channWorkA 完成了剩余的工作")
}

func SelectRoutineA(ch chan int) {
	fmt.Println("SelectRoutineA Do something & return result 1")
	ch <- 1
	fmt.Println("SelectRoutineA Fininshed")
}

func SelectRoutineB(ch chan int) {
	fmt.Println("SelectRoutineB Do something & return result 2")
	ch <- 2
	fmt.Println("SelectRoutineB Fininshed")
}

func ContextFunc(ctx context.Context, tag string) {
	fmt.Printf("%s starts to work!\n", tag)
	for {
		isCanceled := false
		select {
		case <-ctx.Done():
			isCanceled = true
		}
		if isCanceled {
			fmt.Println(tag, ctx.Err())
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func main() {
	{
		// 例1：最简单的情况，没有通道，没有context
		go simpleWork()
		simpleWork()
		fmt.Printf("\n")
		fmt.Println("简单routine完成测试。")
	}

	{
		c := make(chan int)
		// 例2： 使用一个简单的通道沟通两个routines，阻塞式，没有select。
		go channWorkA(c)
		fmt.Println("Main：完成了第一部分的工作，等待channWorkA的计算结果。")
		iValRecvFromChan := <-c
		fmt.Println("Main: 收到了channWorkA的计算结果:", iValRecvFromChan)
		fmt.Println("Main: 继续后续工作。")
	}

	{
		// 例3 用select在main中同时等待多个事件
		// 假设有个任务被分为三块执行，在Main和两个routine中执行，顺序如下：
		// - Main: 做一些前置准备工作
		// - go SelectRoutineA: 做一部分工作得到结果A
		// - go SelectRoutineB: 做另一部分工作得到结果B
		// - Main: 做一些其他事情得到结果C，然后等待SelectRoutineA和SelectRoutineB返回结果
		// - Main: 将A,B,C结果合并
		c1 := make(chan int)
		c2 := make(chan int)

		go SelectRoutineA(c1)
		go SelectRoutineB(c2)

		rA := 0
		rB := 0
		rM := 3

		AFinished := false
		BFinished := false

		for !(AFinished && BFinished) {
			select {
			case rA = <-c1:
				AFinished = true
			case rB = <-c2:
				BFinished = true
			default:
				fmt.Println("blocked ...")
			}
		}

		Res := rA + rB + rM
		fmt.Println("取得结果:", Res)
	}

	{
		//例4：Context cancel
		//在多线程编程中怎么让工作线程优雅退出一直是个大问题，在C++中我们在通常子线程中这样做:
		// While(!m_bQuit){ do some work here. }
		//当主线程需要让子线程退出时，需要将m_bQuit设置为true然后等待工作线程退出即可。
		//以下是GO的实现，主线程依旧在main()中，有两个工作线程ContextFuncA, ContextFuncB
		//当主线程执行cancel()时，同时让ContextFuncA, ContextFuncB都退出。
		ctx, cancelFunc := context.WithCancel(context.Background())
		go ContextFunc(ctx, "Function A")
		go ContextFunc(ctx, "Function B")
		time.Sleep(1 * time.Second)
		fmt.Println("call cancel() in main!")
		cancelFunc()
		time.Sleep(1 * time.Second)
	}

	{
		//例5：Context Timeout
		//和Context Cancel类似，给定一个超时时间，当超时发生时将自动调用cancel()
		ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
		defer cancel()
		go ContextFunc(ctx, "Function A")
		go ContextFunc(ctx, "Function B")
		time.Sleep(2 * time.Second)
	}
}
